/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	EntityDefFactory.java
 * 模块说明：	
 * 修改历史：
 * Mar 20, 2008 - lxm - 创建。
 */
package com.hd123.rumba.runtime.entitydef;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.runtime.CPersistent;
import com.hd123.rumba.util.ObjectUtil;

/**
 * 实体定义工厂
 * 
 * @author lxm
 * 
 */
public class EntityDefFactory {

  private static EntityDefFactory instance = null;

  private EntityDefFactory() {
    super();
  }

  /**
   * 取得当前类的实例。
   * 
   * @return
   */
  public static EntityDefFactory getInstance() {
    if (instance == null)
      instance = new EntityDefFactory();
    return instance;
  }

  /**
   * 载入指定实体的实体定义。
   * 
   * @param defClass
   *            指定对应的实体属性性质类。
   * @param entityClass
   *            指定的实体对象实例。
   * @return
   * @throws BusinessException
   */
  public EntityDef loadEntityDef(Class<CPersistent> defClass, Class entityClass)
      throws BusinessException {
    ObjectUtil.checkParameterNotNull(defClass, "defClass");
    ObjectUtil.checkParameterNotNull(entityClass, "entityClass");

    return doLoadEntityDef(defClass, entityClass);
  }

  private Map<Class, EntityDef> buffer = new HashMap();

  private EntityDef doLoadEntityDef(Class<CPersistent> defClass, Class entityClass)
      throws BusinessException {
    EntityDef def = buffer.get(entityClass);
    if (def != null)
      return def;

    def = new EntityDef();
    def.setClazz(entityClass.getClass());
    def.setDefClass(defClass);
    readEntityInfo(def, defClass);

    for (String propName : getPropertyNames(entityClass)) {
      FieldDef fieldDef = new FieldDef(def);
      fieldDef.setName(propName);
      readFieldInfo(fieldDef, defClass);
      def.getFieldDefs().add(fieldDef);
    }

    buffer.put(entityClass.getClass(), def);
    return def;
  }

  private void readEntityInfo(EntityDef def, Class<CPersistent> defClass) throws BusinessException {
    String caption = (String) this.readFieldValue(defClass, "CAPTION_CLASS");
    if (caption != null)
      def.setCaption(caption);
    else
      def.setCaption(def.getClass().getSimpleName());
  }

  private void readFieldInfo(FieldDef def, Class<CPersistent> defClass) throws BusinessException {
    String upperName = def.getName().toUpperCase();

    // CAPTION_
    String caption = (String) this.readFieldValue(defClass, "CAPTION_" + upperName);
    def.setCaption(caption != null ? caption : def.getName());

    // NOTNULL_
    Boolean notNull = (Boolean) this.readFieldValue(defClass, "NOTNULL_" + upperName);
    if (notNull != null)
      def.setNotNull(notNull.booleanValue());

    // LENGTH_MIN_
    Integer minLength = (Integer) this.readFieldValue(defClass, "LENGTH_MIN_" + upperName);
    if (minLength != null)
      def.setMinLength(minLength.intValue());

    // LENGTH_ | LENGTH_MAX_
    Integer maxLength = (Integer) this.readFieldValue(defClass, "LENGTH_" + upperName);
    if (maxLength == null)
      maxLength = (Integer) this.readFieldValue(defClass, "LENGTH_MAX_" + upperName);
    if (maxLength != null)
      def.setMaxLength(maxLength.intValue());

    // MIN_ | MIN_OPENED_
    Object minValue = this.readFieldValue(defClass, "MIN_" + upperName);
    if (minValue != null) {
      def.setMinValue(minValue);
      def.setMinValueOpened(false);
    } else {
      minValue = this.readFieldValue(defClass, "MIN_OPENED_" + upperName);
      if (minValue != null) {
        def.setMinValue(minValue);
        def.setMinValueOpened(true);
      }
    }

    // MAX_ | MAX_OPENED_
    Object maxValue = this.readFieldValue(defClass, "MAX_" + upperName);
    if (maxValue != null) {
      def.setMaxValue(maxValue);
      def.setMaxValueOpened(false);
    } else {
      maxValue = this.readFieldValue(defClass, "MAX_OPENED_" + upperName);
      if (maxValue != null) {
        def.setMaxValue(maxValue);
        def.setMaxValueOpened(true);
      }
    }

    // 枚举
    Boolean isEnum = (Boolean) this.readFieldValue(defClass, "ISENUM_" + upperName);
    if (isEnum != null)
      def.setEnum(isEnum.booleanValue());
    if (def.isEnum()) {
      List values = this.readFieldValues(defClass, "VALUE_" + upperName + "_");
      def.getEnumValues().addAll(values);
    }
    
    // 服务类
    Class embeddedClass = (Class) this.readFieldValue(defClass, "EMBEDDED_CLASS_" + upperName);
    if (embeddedClass != null) {
      def.setEmbeddedClass(embeddedClass);
    }
  }

  private List<String> getPropertyNames(Class entityClass) throws BusinessException {
    try {
      List<String> propNames = new ArrayList();
      BeanInfo beanInfo = Introspector.getBeanInfo(entityClass);
      for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
        String name = pd.getName();
        if ("class".equals(name))
          continue;
        propNames.add(name);
      }
      return propNames;
    } catch (Exception e) {
      throw new BusinessException(e);
    }
  }

  private Object readFieldValue(Class<CPersistent> defClass, String fieldName)
      throws BusinessException {
    Field f;
    try {
      f = defClass.getField(fieldName);
    } catch (NoSuchFieldException e) {
      return null;
    }
    try {
      return f.get(null);
    } catch (IllegalArgumentException e) {
      throw new BusinessException(e);
    } catch (IllegalAccessException e) {
      throw new BusinessException(e);
    }
  }

  private List readFieldValues(Class<CPersistent> defClass, String fieldNamePrefix)
      throws BusinessException {
    try {
      List list = new ArrayList();
      for (Field f : defClass.getFields()) {
        if (!f.getName().startsWith(fieldNamePrefix))
          continue;
        list.add(f.get(null));
      }
      return list;
    } catch (IllegalArgumentException e) {
      throw new BusinessException(e);
    } catch (IllegalAccessException e) {
      throw new BusinessException(e);
    }
  }
}
